🎖️GitЯра🎖️
Node / meshtastic / Meshtastic-Android / files / feature / firmware / src / commonTest / kotlin / org / meshtastic / feature / firmware / ota / dfu / LegacyDfuTransportTest.kt
Displaying Raw • Download
feature/firmware/src/commonTest/kotlin/org/meshtastic/feature/firmware/ota/dfu/LegacyDfuTransportTest.kt renovate/net.java.dev.jna-jna-5.x (5a8f4966) Text, 23.37 KB
T8b949e/*
* Copyright (c) 2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
Tf0883e@fileTb4b4b4:Te6edf3SuppressTb4b4b4(Ta5d6ff"Ta5d6ffMagicNumberTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffLargeClassTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffTooManyFunctionsTa5d6ff"Tb4b4b4)
Tff7b72package T7ee787org.meshtastic.feature.firmware.ota.dfu
Tff7b72import T7ee787kotlinx.coroutines.CoroutineScope
Tff7b72import T7ee787kotlinx.coroutines.Dispatchers
Tff7b72import T7ee787kotlinx.coroutines.ExperimentalCoroutinesApi
Tff7b72import T7ee787kotlinx.coroutines.flow.Flow
Tff7b72import T7ee787kotlinx.coroutines.flow.StateFlow
Tff7b72import T7ee787kotlinx.coroutines.test.runTest
Tff7b72import T7ee787org.meshtastic.core.ble.BleCharacteristic
Tff7b72import T7ee787org.meshtastic.core.ble.BleConnection
Tff7b72import T7ee787org.meshtastic.core.ble.BleConnectionFactory
Tff7b72import T7ee787org.meshtastic.core.ble.BleConnectionState
Tff7b72import T7ee787org.meshtastic.core.ble.BleDevice
Tff7b72import T7ee787org.meshtastic.core.ble.BleService
Tff7b72import T7ee787org.meshtastic.core.ble.BleWriteType
Tff7b72import T7ee787org.meshtastic.core.testing.FakeBleConnection
Tff7b72import T7ee787org.meshtastic.core.testing.FakeBleConnectionFactory
Tff7b72import T7ee787org.meshtastic.core.testing.FakeBleDevice
Tff7b72import T7ee787org.meshtastic.core.testing.FakeBleScanner
Tff7b72import T7ee787org.meshtastic.core.testing.FakeBleService
Tff7b72import T7ee787org.meshtastic.core.testing.FakeBleWrite
Tff7b72import T7ee787kotlin.test.Test
Tff7b72import T7ee787kotlin.test.assertContentEquals
Tff7b72import T7ee787kotlin.test.assertEquals
Tff7b72import T7ee787kotlin.test.assertIs
Tff7b72import T7ee787kotlin.test.assertTrue
Tff7b72import T7ee787kotlin.time.Duration
Tf0883e@OptInTb4b4b4(Te6edf3ExperimentalCoroutinesApiTff7b72::Te6edf3classTb4b4b4)
Tff7b72class T56d364LegacyDfuTransportTest Tb4b4b4{
Tff7b72private Tff7b72val Te6edf3address Tff7b72= Ta5d6ff"Ta5d6ff00:11:22:33:44:55Ta5d6ff"
Tff7b72private Tff7b72val Te6edf3dfuAddress Tff7b72= Ta5d6ff"Ta5d6ff00:11:22:33:44:56Ta5d6ff"
T8b949e// -----------------------------------------------------------------------
T8b949e// Phase 2: connectToDfuMode
T8b949e// -----------------------------------------------------------------------
Tf0883e@Test
Tff7b72fun Td2a8ff`connectToDfuMode succeeds when bootloader exposes 1530 service`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3scanner Tff7b72= Te6edf3FakeBleScannerTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3connection Tff7b72= Te6edf3FakeBleConnectionTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3transport Tff7b72=
Te6edf3LegacyDfuTransportTb4b4b4(Te6edf3scannerTb4b4b4, Te6edf3FakeBleConnectionFactoryTb4b4b4(Te6edf3connectionTb4b4b4)Tb4b4b4, Te6edf3addressTb4b4b4, Te6edf3DispatchersTb4b4b4.Te6edf3UnconfinedTb4b4b4)
Te6edf3scannerTb4b4b4.Te6edf3emitDeviceTb4b4b4(Te6edf3FakeBleDeviceTb4b4b4(Te6edf3dfuAddressTb4b4b4)Tb4b4b4)
Tff7b72val Te6edf3result Tff7b72= Te6edf3transportTb4b4b4.Te6edf3connectToDfuModeTb4b4b4(Tb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3resultTb4b4b4.Te6edf3isSuccessTb4b4b4, Ta5d6ff"Ta5d6ffconnectToDfuMode failed: Tffd700${Te6edf3resultTb4b4b4.Te6edf3exceptionOrNullTb4b4b4(Tb4b4b4)Tffd700}Ta5d6ff"Tb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`connectToDfuMode fails fast on unsupported old DFU Version lt 5`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3scanner Tff7b72= Te6edf3FakeBleScannerTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3connection Tff7b72= Te6edf3FakeBleConnectionTb4b4b4(Tb4b4b4)
T8b949e// Pre-seed the version characteristic with a SDK 6 version (0x0004 LE).
Te6edf3connectionTb4b4b4.Te6edf3serviceTb4b4b4.Te6edf3enqueueReadTb4b4b4(Te6edf3LEGACY_DFU_VERSION_UUIDTb4b4b4, Te6edf3byteArrayOfTb4b4b4(T79c0ff0Te6edf3x04Tb4b4b4, T79c0ff0Te6edf3x00Tb4b4b4)Tb4b4b4)
Tff7b72val Te6edf3transport Tff7b72=
Te6edf3LegacyDfuTransportTb4b4b4(Te6edf3scannerTb4b4b4, Te6edf3FakeBleConnectionFactoryTb4b4b4(Te6edf3connectionTb4b4b4)Tb4b4b4, Te6edf3addressTb4b4b4, Te6edf3DispatchersTb4b4b4.Te6edf3UnconfinedTb4b4b4)
Te6edf3scannerTb4b4b4.Te6edf3emitDeviceTb4b4b4(Te6edf3FakeBleDeviceTb4b4b4(Te6edf3dfuAddressTb4b4b4)Tb4b4b4)
Tff7b72val Te6edf3result Tff7b72= Te6edf3transportTb4b4b4.Te6edf3connectToDfuModeTb4b4b4(Tb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3resultTb4b4b4.Te6edf3isFailureTb4b4b4)
Te6edf3assertIsTff7b72<Te6edf3LegacyDfuExceptionTb4b4b4.Te6edf3UnsupportedBootloaderTff7b72>Tb4b4b4(Te6edf3resultTb4b4b4.Te6edf3exceptionOrNullTb4b4b4(Tb4b4b4)Tb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`connectToDfuMode accepts modern DFU Version 8`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3scanner Tff7b72= Te6edf3FakeBleScannerTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3connection Tff7b72= Te6edf3FakeBleConnectionTb4b4b4(Tb4b4b4)
Te6edf3connectionTb4b4b4.Te6edf3serviceTb4b4b4.Te6edf3enqueueReadTb4b4b4(Te6edf3LEGACY_DFU_VERSION_UUIDTb4b4b4, Te6edf3byteArrayOfTb4b4b4(T79c0ff0Te6edf3x08Tb4b4b4, T79c0ff0Te6edf3x00Tb4b4b4)Tb4b4b4)
Tff7b72val Te6edf3transport Tff7b72=
Te6edf3LegacyDfuTransportTb4b4b4(Te6edf3scannerTb4b4b4, Te6edf3FakeBleConnectionFactoryTb4b4b4(Te6edf3connectionTb4b4b4)Tb4b4b4, Te6edf3addressTb4b4b4, Te6edf3DispatchersTb4b4b4.Te6edf3UnconfinedTb4b4b4)
Te6edf3scannerTb4b4b4.Te6edf3emitDeviceTb4b4b4(Te6edf3FakeBleDeviceTb4b4b4(Te6edf3dfuAddressTb4b4b4)Tb4b4b4)
Tff7b72val Te6edf3result Tff7b72= Te6edf3transportTb4b4b4.Te6edf3connectToDfuModeTb4b4b4(Tb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3resultTb4b4b4.Te6edf3isSuccessTb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`connectToDfuMode accepts missing DFU Version characteristic`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
T8b949e// Default FakeBleService.read returns empty bytes when nothing is enqueued — treated as "absent".
Tff7b72val Te6edf3scanner Tff7b72= Te6edf3FakeBleScannerTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3connection Tff7b72= Te6edf3FakeBleConnectionTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3transport Tff7b72=
Te6edf3LegacyDfuTransportTb4b4b4(Te6edf3scannerTb4b4b4, Te6edf3FakeBleConnectionFactoryTb4b4b4(Te6edf3connectionTb4b4b4)Tb4b4b4, Te6edf3addressTb4b4b4, Te6edf3DispatchersTb4b4b4.Te6edf3UnconfinedTb4b4b4)
Te6edf3scannerTb4b4b4.Te6edf3emitDeviceTb4b4b4(Te6edf3FakeBleDeviceTb4b4b4(Te6edf3dfuAddressTb4b4b4)Tb4b4b4)
Tff7b72val Te6edf3result Tff7b72= Te6edf3transportTb4b4b4.Te6edf3connectToDfuModeTb4b4b4(Tb4b4b4)
Te6edf3assertTrueTb4b4b4(
Te6edf3resultTb4b4b4.Te6edf3isSuccessTb4b4b4,
Ta5d6ff"Ta5d6ffMissing DFU Version should be treated as modern (proceed): Tffd700${Te6edf3resultTb4b4b4.Te6edf3exceptionOrNullTb4b4b4(Tb4b4b4)Tffd700}Ta5d6ff"Tb4b4b4,
Tb4b4b4)
Tb4b4b4}
T8b949e// -----------------------------------------------------------------------
T8b949e// Phase 3: transferInitPacket preflight
T8b949e// -----------------------------------------------------------------------
Tf0883e@Test
Tff7b72fun Td2a8ff`transferInitPacket rejects oversized init packet looks like Secure-shaped dat`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3transport Tff7b72=
Te6edf3LegacyDfuTransportTb4b4b4(
Te6edf3FakeBleScannerTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3FakeBleConnectionFactoryTb4b4b4(Te6edf3FakeBleConnectionTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4,
Te6edf3addressTb4b4b4,
Te6edf3DispatchersTb4b4b4.Te6edf3UnconfinedTb4b4b4,
Tb4b4b4)
Tff7b72val Te6edf3oversized Tff7b72= Te6edf3ByteArrayTb4b4b4(Te6edf3LegacyDfuTransportTb4b4b4.Te6edf3MAX_REASONABLE_LEGACY_INIT_SIZE Tff7b72+ T79c0ff1Tb4b4b4) Tb4b4b4{ T79c0ff0Te6edf3x42 Tb4b4b4}
Tff7b72val Te6edf3result Tff7b72= Te6edf3transportTb4b4b4.Te6edf3transferInitPacketTb4b4b4(Te6edf3oversizedTb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3resultTb4b4b4.Te6edf3isFailureTb4b4b4)
Te6edf3assertIsTff7b72<Te6edf3LegacyDfuExceptionTb4b4b4.Te6edf3InitPacketNotLegacyTff7b72>Tb4b4b4(Te6edf3resultTb4b4b4.Te6edf3exceptionOrNullTb4b4b4(Tb4b4b4)Tb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`transferInitPacket accepts typical 14 byte legacy init`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3transport Tff7b72=
Te6edf3LegacyDfuTransportTb4b4b4(
Te6edf3FakeBleScannerTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3FakeBleConnectionFactoryTb4b4b4(Te6edf3FakeBleConnectionTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4,
Te6edf3addressTb4b4b4,
Te6edf3DispatchersTb4b4b4.Te6edf3UnconfinedTb4b4b4,
Tb4b4b4)
Tff7b72val Te6edf3init Tff7b72= Te6edf3ByteArrayTb4b4b4(T79c0ff1T79c0ff4Tb4b4b4) Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3toByteTb4b4b4(Tb4b4b4) Tb4b4b4}
Tff7b72val Te6edf3result Tff7b72= Te6edf3transportTb4b4b4.Te6edf3transferInitPacketTb4b4b4(Tff7b72initTb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3resultTb4b4b4.Te6edf3isSuccessTb4b4b4)
Tb4b4b4}
T8b949e// -----------------------------------------------------------------------
T8b949e// Phase 4: transferFirmware happy path
T8b949e// -----------------------------------------------------------------------
Tf0883e@Test
Tff7b72fun Td2a8ff`transferFirmware happy path writes correct opcode and packet sequence`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3env Tff7b72= Te6edf3createConnectedTransportTb4b4b4(Tb4b4b4)
Te6edf3envTb4b4b4.Te6edf3responderTb4b4b4.Te6edf3scheme Tff7b72= Te6edf3LegacyResponderSchemeTb4b4b4.Te6edf3HappyPath
Tff7b72val Te6edf3init Tff7b72= Te6edf3ByteArrayTb4b4b4(T79c0ff1T79c0ff4Tb4b4b4) Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3toByteTb4b4b4(Tb4b4b4) Tb4b4b4}
Tff7b72val Te6edf3firmware Tff7b72= Te6edf3ByteArrayTb4b4b4(T79c0ff8T79c0ff0Tb4b4b4) Tb4b4b4{ Tb4b4b4(T79c0ff0Te6edf3xA0 Tff7b72+ Tffa657itTb4b4b4)Tb4b4b4.Te6edf3toByteTb4b4b4(Tb4b4b4) Tb4b4b4} T8b949e// 4 packets at MTU=20
Te6edf3envTb4b4b4.Te6edf3transportTb4b4b4.Te6edf3transferInitPacketTb4b4b4(Tff7b72initTb4b4b4)Tb4b4b4.Te6edf3getOrThrowTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3progress Tff7b72= Te6edf3mutableListOfTff7b72<Tffa657FloatTff7b72>Tb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3result Tff7b72= Te6edf3envTb4b4b4.Te6edf3transportTb4b4b4.Te6edf3transferFirmwareTb4b4b4(Te6edf3firmwareTb4b4b4) Tb4b4b4{ Te6edf3progressTb4b4b4.Te6edf3addTb4b4b4(Tffa657itTb4b4b4) Tb4b4b4}
Te6edf3assertTrueTb4b4b4(Te6edf3resultTb4b4b4.Te6edf3isSuccessTb4b4b4, Ta5d6ff"Ta5d6fftransferFirmware failed: Tffd700${Te6edf3resultTb4b4b4.Te6edf3exceptionOrNullTb4b4b4(Tb4b4b4)Tffd700}Ta5d6ff"Tb4b4b4)
T8b949e// Control-point opcode order:
T8b949e// START_DFU, INIT_DFU_PARAMS_START, INIT_DFU_PARAMS_COMPLETE,
T8b949e// PACKET_RECEIPT_NOTIF_REQ, RECEIVE_FIRMWARE_IMAGE, VALIDATE, ACTIVATE_AND_RESET
Tff7b72val Te6edf3cpOpcodes Tff7b72= Te6edf3envTb4b4b4.Te6edf3controlPointWritesTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3map Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3dataTff7b72[T79c0ff0Tff7b72] Tb4b4b4}
Te6edf3assertEqualsTb4b4b4(
Te6edf3listOfTb4b4b4(
Te6edf3LegacyDfuOpcodeTb4b4b4.Te6edf3START_DFUTb4b4b4,
Te6edf3LegacyDfuOpcodeTb4b4b4.Te6edf3INIT_DFU_PARAMSTb4b4b4,
Te6edf3LegacyDfuOpcodeTb4b4b4.Te6edf3INIT_DFU_PARAMSTb4b4b4,
Te6edf3LegacyDfuOpcodeTb4b4b4.Te6edf3PACKET_RECEIPT_NOTIF_REQTb4b4b4,
Te6edf3LegacyDfuOpcodeTb4b4b4.Te6edf3RECEIVE_FIRMWARE_IMAGETb4b4b4,
Te6edf3LegacyDfuOpcodeTb4b4b4.Te6edf3VALIDATETb4b4b4,
Te6edf3LegacyDfuOpcodeTb4b4b4.Te6edf3ACTIVATE_AND_RESETTb4b4b4,
Tb4b4b4)Tb4b4b4,
Te6edf3cpOpcodesTb4b4b4,
Tb4b4b4)
T8b949e// First INIT_DFU_PARAMS sub-opcode is START (0x00), second is COMPLETE (0x01).
Tff7b72val Te6edf3initParamsWrites Tff7b72= Te6edf3envTb4b4b4.Te6edf3controlPointWritesTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3filter Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3dataTff7b72[T79c0ff0Tff7b72] Tff7b72=Tff7b72= Te6edf3LegacyDfuOpcodeTb4b4b4.Te6edf3INIT_DFU_PARAMS Tb4b4b4}
Te6edf3assertEqualsTb4b4b4(Te6edf3LegacyDfuOpcodeTb4b4b4.Te6edf3INIT_PARAMS_STARTTb4b4b4, Te6edf3initParamsWritesTff7b72[T79c0ff0Tff7b72]Tb4b4b4.Te6edf3dataTff7b72[T79c0ff1Tff7b72]Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(Te6edf3LegacyDfuOpcodeTb4b4b4.Te6edf3INIT_PARAMS_COMPLETETb4b4b4, Te6edf3initParamsWritesTff7b72[T79c0ff1Tff7b72]Tb4b4b4.Te6edf3dataTff7b72[T79c0ff1Tff7b72]Tb4b4b4)
T8b949e// START_DFU includes APP image type byte.
Tff7b72val Te6edf3startWrite Tff7b72= Te6edf3envTb4b4b4.Te6edf3controlPointWritesTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3single Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3dataTff7b72[T79c0ff0Tff7b72] Tff7b72=Tff7b72= Te6edf3LegacyDfuOpcodeTb4b4b4.Te6edf3START_DFU Tb4b4b4}
Te6edf3assertContentEqualsTb4b4b4(Te6edf3byteArrayOfTb4b4b4(Te6edf3LegacyDfuOpcodeTb4b4b4.Te6edf3START_DFUTb4b4b4, Te6edf3LegacyDfuImageTypeTb4b4b4.Te6edf3APPLICATIONTb4b4b4)Tb4b4b4, Te6edf3startWriteTb4b4b4.Te6edf3dataTb4b4b4)
T8b949e// Packet writes should contain: [12B image sizes] then [14B init in chunks] then [firmware in chunks].
Tff7b72val Te6edf3packetBytes Tff7b72= Te6edf3envTb4b4b4.Te6edf3packetWritesTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3flatMap Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3dataTb4b4b4.Te6edf3toListTb4b4b4(Tb4b4b4) Tb4b4b4}Tb4b4b4.Te6edf3toByteArrayTb4b4b4(Tb4b4b4)
T8b949e// Image sizes payload (first 12 bytes): app size = firmware.size, others 0.
Tff7b72val Te6edf3imageSizes Tff7b72= Te6edf3packetBytesTb4b4b4.Te6edf3copyOfRangeTb4b4b4(T79c0ff0Tb4b4b4, T79c0ff1T79c0ff2Tb4b4b4)
Te6edf3assertContentEqualsTb4b4b4(Te6edf3legacyImageSizesPayloadTb4b4b4(Te6edf3appSize Tff7b72= Te6edf3firmwareTb4b4b4.Te6edf3sizeTb4b4b4)Tb4b4b4, Te6edf3imageSizesTb4b4b4)
T8b949e// Init follows.
Te6edf3assertContentEqualsTb4b4b4(Tff7b72initTb4b4b4, Te6edf3packetBytesTb4b4b4.Te6edf3copyOfRangeTb4b4b4(T79c0ff1T79c0ff2Tb4b4b4, T79c0ff1T79c0ff2 Tff7b72+ Tff7b72initTb4b4b4.Te6edf3sizeTb4b4b4)Tb4b4b4)
T8b949e// Firmware follows.
Te6edf3assertContentEqualsTb4b4b4(Te6edf3firmwareTb4b4b4, Te6edf3packetBytesTb4b4b4.Te6edf3copyOfRangeTb4b4b4(T79c0ff1T79c0ff2 Tff7b72+ Tff7b72initTb4b4b4.Te6edf3sizeTb4b4b4, Te6edf3packetBytesTb4b4b4.Te6edf3sizeTb4b4b4)Tb4b4b4)
T8b949e// Final progress should be 1.0.
Te6edf3assertEqualsTb4b4b4(T79c0ff1fTb4b4b4, Te6edf3progressTb4b4b4.Te6edf3lastTb4b4b4(Tb4b4b4)Tb4b4b4)
T8b949e// Packet writes use WITHOUT_RESPONSE.
Te6edf3envTb4b4b4.Te6edf3packetWritesTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3forEach Tb4b4b4{ Te6edf3assertEqualsTb4b4b4(Te6edf3BleWriteTypeTb4b4b4.Te6edf3WITHOUT_RESPONSETb4b4b4, Tffa657itTb4b4b4.Te6edf3writeTypeTb4b4b4) Tb4b4b4}
Tb4b4b4}
T8b949e// -----------------------------------------------------------------------
T8b949e// Phase 4: error paths
T8b949e// -----------------------------------------------------------------------
Tf0883e@Test
Tff7b72fun Td2a8ff`transferFirmware fails with ProtocolError when device rejects START_DFU`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3env Tff7b72= Te6edf3createConnectedTransportTb4b4b4(Tb4b4b4)
Te6edf3envTb4b4b4.Te6edf3responderTb4b4b4.Te6edf3scheme Tff7b72= Te6edf3LegacyResponderSchemeTb4b4b4.Te6edf3RejectStart
Te6edf3envTb4b4b4.Te6edf3transportTb4b4b4.Te6edf3transferInitPacketTb4b4b4(Te6edf3ByteArrayTb4b4b4(T79c0ff1T79c0ff4Tb4b4b4)Tb4b4b4)Tb4b4b4.Te6edf3getOrThrowTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3result Tff7b72= Te6edf3envTb4b4b4.Te6edf3transportTb4b4b4.Te6edf3transferFirmwareTb4b4b4(Te6edf3ByteArrayTb4b4b4(T79c0ff4T79c0ff0Tb4b4b4)Tb4b4b4) Tb4b4b4{Tb4b4b4}
Te6edf3assertTrueTb4b4b4(Te6edf3resultTb4b4b4.Te6edf3isFailureTb4b4b4)
Tff7b72val Te6edf3ex Tff7b72= Te6edf3assertIsTff7b72<Te6edf3LegacyDfuExceptionTb4b4b4.Te6edf3ProtocolErrorTff7b72>Tb4b4b4(Te6edf3resultTb4b4b4.Te6edf3exceptionOrNullTb4b4b4(Tb4b4b4)Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(Te6edf3LegacyDfuOpcodeTb4b4b4.Te6edf3START_DFUTb4b4b4, Te6edf3exTb4b4b4.Te6edf3requestOpcodeTb4b4b4)
Te6edf3assertEqualsTb4b4b4(Te6edf3LegacyDfuStatusTb4b4b4.Te6edf3NOT_SUPPORTEDTb4b4b4, Te6edf3exTb4b4b4.Te6edf3statusTb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`transferFirmware fails with ProtocolError when device rejects INIT_DFU_PARAMS`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3env Tff7b72= Te6edf3createConnectedTransportTb4b4b4(Tb4b4b4)
Te6edf3envTb4b4b4.Te6edf3responderTb4b4b4.Te6edf3scheme Tff7b72= Te6edf3LegacyResponderSchemeTb4b4b4.Te6edf3RejectInit
Te6edf3envTb4b4b4.Te6edf3transportTb4b4b4.Te6edf3transferInitPacketTb4b4b4(Te6edf3ByteArrayTb4b4b4(T79c0ff1T79c0ff4Tb4b4b4)Tb4b4b4)Tb4b4b4.Te6edf3getOrThrowTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3result Tff7b72= Te6edf3envTb4b4b4.Te6edf3transportTb4b4b4.Te6edf3transferFirmwareTb4b4b4(Te6edf3ByteArrayTb4b4b4(T79c0ff4T79c0ff0Tb4b4b4)Tb4b4b4) Tb4b4b4{Tb4b4b4}
Te6edf3assertTrueTb4b4b4(Te6edf3resultTb4b4b4.Te6edf3isFailureTb4b4b4)
Tff7b72val Te6edf3ex Tff7b72= Te6edf3assertIsTff7b72<Te6edf3LegacyDfuExceptionTb4b4b4.Te6edf3ProtocolErrorTff7b72>Tb4b4b4(Te6edf3resultTb4b4b4.Te6edf3exceptionOrNullTb4b4b4(Tb4b4b4)Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(Te6edf3LegacyDfuOpcodeTb4b4b4.Te6edf3INIT_DFU_PARAMSTb4b4b4, Te6edf3exTb4b4b4.Te6edf3requestOpcodeTb4b4b4)
Te6edf3assertEqualsTb4b4b4(Te6edf3LegacyDfuStatusTb4b4b4.Te6edf3OPERATION_FAILEDTb4b4b4, Te6edf3exTb4b4b4.Te6edf3statusTb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`transferFirmware fails with ProtocolError when device rejects VALIDATE`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3env Tff7b72= Te6edf3createConnectedTransportTb4b4b4(Tb4b4b4)
Te6edf3envTb4b4b4.Te6edf3responderTb4b4b4.Te6edf3scheme Tff7b72= Te6edf3LegacyResponderSchemeTb4b4b4.Te6edf3RejectValidate
Te6edf3envTb4b4b4.Te6edf3transportTb4b4b4.Te6edf3transferInitPacketTb4b4b4(Te6edf3ByteArrayTb4b4b4(T79c0ff1T79c0ff4Tb4b4b4)Tb4b4b4)Tb4b4b4.Te6edf3getOrThrowTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3result Tff7b72= Te6edf3envTb4b4b4.Te6edf3transportTb4b4b4.Te6edf3transferFirmwareTb4b4b4(Te6edf3ByteArrayTb4b4b4(T79c0ff4T79c0ff0Tb4b4b4)Tb4b4b4) Tb4b4b4{Tb4b4b4}
Te6edf3assertTrueTb4b4b4(Te6edf3resultTb4b4b4.Te6edf3isFailureTb4b4b4)
Tff7b72val Te6edf3ex Tff7b72= Te6edf3assertIsTff7b72<Te6edf3LegacyDfuExceptionTb4b4b4.Te6edf3ProtocolErrorTff7b72>Tb4b4b4(Te6edf3resultTb4b4b4.Te6edf3exceptionOrNullTb4b4b4(Tb4b4b4)Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(Te6edf3LegacyDfuOpcodeTb4b4b4.Te6edf3VALIDATETb4b4b4, Te6edf3exTb4b4b4.Te6edf3requestOpcodeTb4b4b4)
Te6edf3assertEqualsTb4b4b4(Te6edf3LegacyDfuStatusTb4b4b4.Te6edf3CRC_ERRORTb4b4b4, Te6edf3exTb4b4b4.Te6edf3statusTb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`transferFirmware fails with PacketReceiptMismatch when device under-reports`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3env Tff7b72= Te6edf3createConnectedTransportTb4b4b4(Tb4b4b4)
Te6edf3envTb4b4b4.Te6edf3responderTb4b4b4.Te6edf3scheme Tff7b72= Te6edf3LegacyResponderSchemeTb4b4b4.Te6edf3PrnUnderReport
Te6edf3envTb4b4b4.Te6edf3transportTb4b4b4.Te6edf3transferInitPacketTb4b4b4(Te6edf3ByteArrayTb4b4b4(T79c0ff1T79c0ff4Tb4b4b4)Tb4b4b4)Tb4b4b4.Te6edf3getOrThrowTb4b4b4(Tb4b4b4)
T8b949e// Send (PRN_INTERVAL_PACKETS + 1) packets of 20 bytes — guarantees a PRN window fires
T8b949e// before the firmware completes, so the under-reported byte count surfaces as a mismatch.
Tff7b72val Te6edf3firmwareSize Tff7b72= Tb4b4b4(Te6edf3LegacyDfuTransportTb4b4b4.Te6edf3PRN_INTERVAL_PACKETS Tff7b72+ T79c0ff1Tb4b4b4) Tff7b72* T79c0ff2T79c0ff0
Tff7b72val Te6edf3result Tff7b72= Te6edf3envTb4b4b4.Te6edf3transportTb4b4b4.Te6edf3transferFirmwareTb4b4b4(Te6edf3ByteArrayTb4b4b4(Te6edf3firmwareSizeTb4b4b4)Tb4b4b4) Tb4b4b4{Tb4b4b4}
Te6edf3assertTrueTb4b4b4(Te6edf3resultTb4b4b4.Te6edf3isFailureTb4b4b4)
Te6edf3assertIsTff7b72<Te6edf3LegacyDfuExceptionTb4b4b4.Te6edf3PacketReceiptMismatchTff7b72>Tb4b4b4(Te6edf3resultTb4b4b4.Te6edf3exceptionOrNullTb4b4b4(Tb4b4b4)Tb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`transferFirmware tolerates ACTIVATE_AND_RESET write failure - disconnect race`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3env Tff7b72= Te6edf3createConnectedTransportTb4b4b4(Tb4b4b4)
Te6edf3envTb4b4b4.Te6edf3responderTb4b4b4.Te6edf3scheme Tff7b72= Te6edf3LegacyResponderSchemeTb4b4b4.Te6edf3HappyPath
T8b949e// After VALIDATE response is sent, ACTIVATE write should be treated as success even if the device throws.
Te6edf3envTb4b4b4.Te6edf3responderTb4b4b4.Te6edf3failOnActivateWrite Tff7b72= Tff7b72true
Te6edf3envTb4b4b4.Te6edf3transportTb4b4b4.Te6edf3transferInitPacketTb4b4b4(Te6edf3ByteArrayTb4b4b4(T79c0ff1T79c0ff4Tb4b4b4)Tb4b4b4)Tb4b4b4.Te6edf3getOrThrowTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3result Tff7b72= Te6edf3envTb4b4b4.Te6edf3transportTb4b4b4.Te6edf3transferFirmwareTb4b4b4(Te6edf3ByteArrayTb4b4b4(T79c0ff4T79c0ff0Tb4b4b4)Tb4b4b4) Tb4b4b4{Tb4b4b4}
Te6edf3assertTrueTb4b4b4(
Te6edf3resultTb4b4b4.Te6edf3isSuccessTb4b4b4,
Ta5d6ff"Ta5d6ffACTIVATE write failure must be treated as success; got: Tffd700${Te6edf3resultTb4b4b4.Te6edf3exceptionOrNullTb4b4b4(Tb4b4b4)Tffd700}Ta5d6ff"Tb4b4b4,
Tb4b4b4)
Tb4b4b4}
T8b949e// -----------------------------------------------------------------------
T8b949e// Abort
T8b949e// -----------------------------------------------------------------------
Tf0883e@Test
Tff7b72fun Td2a8ff`abort writes RESET opcode through control point`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3connection Tff7b72= Te6edf3FakeBleConnectionTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3transport Tff7b72=
Te6edf3LegacyDfuTransportTb4b4b4(Te6edf3FakeBleScannerTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3FakeBleConnectionFactoryTb4b4b4(Te6edf3connectionTb4b4b4)Tb4b4b4, Te6edf3addressTb4b4b4, Te6edf3DispatchersTb4b4b4.Te6edf3UnconfinedTb4b4b4)
Te6edf3transportTb4b4b4.Te6edf3abortTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3write Tff7b72= Te6edf3connectionTb4b4b4.Te6edf3serviceTb4b4b4.Te6edf3writesTb4b4b4.Te6edf3singleTb4b4b4(Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(Te6edf3LegacyDfuUuidsTb4b4b4.Te6edf3CONTROL_POINTTb4b4b4, Te6edf3writeTb4b4b4.Te6edf3characteristicTb4b4b4.Te6edf3uuidTb4b4b4)
Te6edf3assertContentEqualsTb4b4b4(Te6edf3byteArrayOfTb4b4b4(Te6edf3LegacyDfuOpcodeTb4b4b4.Te6edf3RESETTb4b4b4)Tb4b4b4, Te6edf3writeTb4b4b4.Te6edf3dataTb4b4b4)
Te6edf3assertEqualsTb4b4b4(Te6edf3BleWriteTypeTb4b4b4.Te6edf3WITH_RESPONSETb4b4b4, Te6edf3writeTb4b4b4.Te6edf3writeTypeTb4b4b4)
Tb4b4b4}
T8b949e// -----------------------------------------------------------------------
T8b949e// Test infrastructure
T8b949e// -----------------------------------------------------------------------
Tff7b72private Tff7b72class T56d364TestEnvTb4b4b4(Tff7b72val Te6edf3transportTb4b4b4: Te6edf3LegacyDfuTransportTb4b4b4, Tff7b72val Te6edf3serviceTb4b4b4: Te6edf3AutoRespondingLegacyServiceTb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3responder Tff7b72= Te6edf3serviceTb4b4b4.Te6edf3responder
Tff7b72fun Td2a8ffcontrolPointWritesTb4b4b4(Tb4b4b4)Tb4b4b4: Te6edf3ListTff7b72<Te6edf3FakeBleWriteTff7b72> Tff7b72=
Te6edf3serviceTb4b4b4.Te6edf3delegateTb4b4b4.Te6edf3writesTb4b4b4.Te6edf3filter Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3characteristicTb4b4b4.Te6edf3uuid Tff7b72=Tff7b72= Te6edf3LegacyDfuUuidsTb4b4b4.Te6edf3CONTROL_POINT Tb4b4b4}
Tff7b72fun Td2a8ffpacketWritesTb4b4b4(Tb4b4b4)Tb4b4b4: Te6edf3ListTff7b72<Te6edf3FakeBleWriteTff7b72> Tff7b72=
Te6edf3serviceTb4b4b4.Te6edf3delegateTb4b4b4.Te6edf3writesTb4b4b4.Te6edf3filter Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3characteristicTb4b4b4.Te6edf3uuid Tff7b72=Tff7b72= Te6edf3LEGACY_DFU_PACKET_UUID Tb4b4b4}
Tb4b4b4}
Tff7b72private Tff7b72suspend Tff7b72fun Td2a8ffcreateConnectedTransportTb4b4b4(Te6edf3mtuTb4b4b4: Tffa657Int? Tff7b72= Tff7b72nullTb4b4b4)Tb4b4b4: Te6edf3TestEnv Tb4b4b4{
Tff7b72val Te6edf3scanner Tff7b72= Te6edf3FakeBleScannerTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3fakeConnection Tff7b72= Te6edf3FakeBleConnectionTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3apply Tb4b4b4{ Te6edf3maxWriteValueLength Tff7b72= Te6edf3mtu Tb4b4b4}
Tff7b72val Te6edf3autoService Tff7b72= Te6edf3AutoRespondingLegacyServiceTb4b4b4(Te6edf3fakeConnectionTb4b4b4.Te6edf3serviceTb4b4b4)
Tff7b72val Te6edf3wrappedConnection Tff7b72= Te6edf3AutoRespondingBleConnectionTb4b4b4(Te6edf3fakeConnectionTb4b4b4, Te6edf3autoServiceTb4b4b4)
Tff7b72val Te6edf3factory Tff7b72=
Tff7b72object Tb4b4b4: T56d364BleConnectionFactory Tb4b4b4{
Tff7b72override Tff7b72fun Td2a8ffcreateTb4b4b4(Te6edf3scopeTb4b4b4: Te6edf3CoroutineScopeTb4b4b4, Te6edf3tagTb4b4b4: Tffa657StringTb4b4b4)Tb4b4b4: Te6edf3BleConnection Tff7b72= Te6edf3wrappedConnection
Tb4b4b4}
Tff7b72val Te6edf3transport Tff7b72= Te6edf3LegacyDfuTransportTb4b4b4(Te6edf3scannerTb4b4b4, Te6edf3factoryTb4b4b4, Te6edf3addressTb4b4b4, Te6edf3DispatchersTb4b4b4.Te6edf3UnconfinedTb4b4b4)
Te6edf3scannerTb4b4b4.Te6edf3emitDeviceTb4b4b4(Te6edf3FakeBleDeviceTb4b4b4(Te6edf3dfuAddressTb4b4b4)Tb4b4b4)
Te6edf3transportTb4b4b4.Te6edf3connectToDfuModeTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3getOrThrowTb4b4b4(Tb4b4b4)
Tff7b72return Te6edf3TestEnvTb4b4b4(Te6edf3transportTb4b4b4, Te6edf3autoServiceTb4b4b4)
Tb4b4b4}
T8b949e/**
* Drives the simulated bootloader response stream. After each `write()` to control point or packet, this service
* synthesises the appropriate notification(s) on the control-point characteristic so the transport's pending
* `awaitResponse` / `awaitPacketReceipt` calls unblock.
*/
Tff7b72private Tff7b72class T56d364AutoRespondingLegacyServiceTb4b4b4(Tff7b72val Te6edf3delegateTb4b4b4: Te6edf3FakeBleServiceTb4b4b4) Tb4b4b4: Te6edf3BleService Tb4b4b4{
Tff7b72val Te6edf3responder Tff7b72= Te6edf3LegacyResponderTb4b4b4(Tb4b4b4)
Tff7b72override Tff7b72fun Td2a8ffhasCharacteristicTb4b4b4(Te6edf3cTb4b4b4: Te6edf3BleCharacteristicTb4b4b4) Tff7b72= Te6edf3delegateTb4b4b4.Te6edf3hasCharacteristicTb4b4b4(Te6edf3cTb4b4b4)
Tff7b72override Tff7b72fun Td2a8ffobserveTb4b4b4(Te6edf3cTb4b4b4: Te6edf3BleCharacteristicTb4b4b4)Tb4b4b4: Te6edf3FlowTff7b72<Te6edf3ByteArrayTff7b72> Tff7b72= Te6edf3delegateTb4b4b4.Te6edf3observeTb4b4b4(Te6edf3cTb4b4b4)
Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffreadTb4b4b4(Te6edf3cTb4b4b4: Te6edf3BleCharacteristicTb4b4b4)Tb4b4b4: Te6edf3ByteArray Tff7b72= Te6edf3delegateTb4b4b4.Te6edf3readTb4b4b4(Te6edf3cTb4b4b4)
Tff7b72override Tff7b72fun Td2a8ffpreferredWriteTypeTb4b4b4(Te6edf3cTb4b4b4: Te6edf3BleCharacteristicTb4b4b4)Tb4b4b4: Te6edf3BleWriteType Tff7b72= Te6edf3delegateTb4b4b4.Te6edf3preferredWriteTypeTb4b4b4(Te6edf3cTb4b4b4)
Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffwriteTb4b4b4(Te6edf3cTb4b4b4: Te6edf3BleCharacteristicTb4b4b4, Te6edf3dataTb4b4b4: Te6edf3ByteArrayTb4b4b4, Te6edf3writeTypeTb4b4b4: Te6edf3BleWriteTypeTb4b4b4) Tb4b4b4{
Te6edf3delegateTb4b4b4.Te6edf3writeTb4b4b4(Te6edf3cTb4b4b4, Te6edf3dataTb4b4b4, Te6edf3writeTypeTb4b4b4)
Tff7b72val Te6edf3response Tff7b72= Te6edf3responderTb4b4b4.Te6edf3onWriteTb4b4b4(Te6edf3cTb4b4b4.Te6edf3uuidTb4b4b4, Te6edf3dataTb4b4b4) Tff7b72?: Tff7b72return
Te6edf3responseTb4b4b4.Te6edf3forEach Tb4b4b4{ Te6edf3delegateTb4b4b4.Te6edf3emitNotificationTb4b4b4(Te6edf3LegacyDfuUuidsTb4b4b4.Te6edf3CONTROL_POINTTb4b4b4, Tffa657itTb4b4b4) Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
T8b949e/** What the simulated bootloader is meant to do for this test case. */
Tff7b72enum Tff7b72class T56d364LegacyResponderScheme Tb4b4b4{
Te6edf3HappyPathTb4b4b4,
Te6edf3RejectStartTb4b4b4,
Te6edf3RejectInitTb4b4b4,
Te6edf3RejectValidateTb4b4b4,
Te6edf3PrnUnderReportTb4b4b4,
Tb4b4b4}
T8b949e/**
* Synthesises Legacy DFU notifications based on the transport's current write. Behaviour depends on [scheme]:
* - `HappyPath`: returns Success for every control-point opcode that expects a response, plus accurate PRN receipts
* as packets accumulate.
* - Reject* variants: return Failure with the indicated status for the targeted opcode.
* - `PrnUnderReport`: at the first PRN window, report bytesReceived = actual − 1 to trigger PacketReceiptMismatch.
*
* Image-sizes write (12B on packet) is the trigger for the START_DFU response — matching the real protocol where
* the device responds *after* it sees both the opcode and the size payload.
*/
Tff7b72class T56d364LegacyResponder Tb4b4b4{
Tff7b72var Te6edf3schemeTb4b4b4: Te6edf3LegacyResponderScheme Tff7b72= Te6edf3LegacyResponderSchemeTb4b4b4.Te6edf3HappyPath
Tff7b72var Te6edf3failOnActivateWriteTb4b4b4: Tffa657Boolean Tff7b72= Tff7b72false
Tff7b72private Tff7b72var Te6edf3packetBytesReceived Tff7b72= T79c0ff0L
Tff7b72private Tff7b72var Te6edf3packetsSinceLastPrn Tff7b72= T79c0ff0
Tff7b72private Tff7b72var Te6edf3firmwareTransferStarted Tff7b72= Tff7b72false
Tff7b72private Tff7b72var Te6edf3imageSizesWritten Tff7b72= Tff7b72false
Tff7b72private Tff7b72var Te6edf3expectedFirmwareSizeTb4b4b4: Tffa657Int Tff7b72= T79c0ff0
Tff7b72fun Td2a8ffonWriteTb4b4b4(Te6edf3uuidTb4b4b4: Te6edf3kotlinTb4b4b4.Te6edf3uuidTb4b4b4.Te6edf3UuidTb4b4b4, Te6edf3dataTb4b4b4: Te6edf3ByteArrayTb4b4b4)Tb4b4b4: Te6edf3ListTff7b72<Te6edf3ByteArrayTff7b72>Tff7b72? Tff7b72= Tff7b72when Tb4b4b4(Te6edf3uuidTb4b4b4) Tb4b4b4{
Te6edf3LegacyDfuUuidsTb4b4b4.Te6edf3CONTROL_POINT Tff7b72-Tff7b72> Te6edf3handleControlWriteTb4b4b4(Te6edf3dataTb4b4b4)
Te6edf3LEGACY_DFU_PACKET_UUID Tff7b72-Tff7b72> Te6edf3handlePacketWriteTb4b4b4(Te6edf3dataTb4b4b4)
Tff7b72else Tff7b72-Tff7b72> Tff7b72null
Tb4b4b4}
Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffReturnCountTa5d6ff"Tb4b4b4)
Tff7b72private Tff7b72fun Td2a8ffhandleControlWriteTb4b4b4(Te6edf3dataTb4b4b4: Te6edf3ByteArrayTb4b4b4)Tb4b4b4: Te6edf3ListTff7b72<Te6edf3ByteArrayTff7b72>Tff7b72? Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3dataTb4b4b4.Te6edf3isEmptyTb4b4b4(Tb4b4b4)Tb4b4b4) Tff7b72return Tff7b72null
Tff7b72val Te6edf3opcode Tff7b72= Te6edf3dataTff7b72[T79c0ff0Tff7b72]
Tff7b72return Tff7b72when Tb4b4b4(Te6edf3opcodeTb4b4b4) Tb4b4b4{
Te6edf3LegacyDfuOpcodeTb4b4b4.Te6edf3START_DFU Tff7b72-Tff7b72> Tff7b72null
T8b949e// response comes after image sizes packet write
Te6edf3LegacyDfuOpcodeTb4b4b4.Te6edf3INIT_DFU_PARAMS Tff7b72-Tff7b72> Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3dataTb4b4b4.Te6edf3size Tff7b72>Tff7b72= T79c0ff2 Tff7b72&Tff7b72& Te6edf3dataTff7b72[T79c0ff1Tff7b72] Tff7b72=Tff7b72= Te6edf3LegacyDfuOpcodeTb4b4b4.Te6edf3INIT_PARAMS_COMPLETETb4b4b4) Tb4b4b4{
Te6edf3listOfTb4b4b4(Te6edf3initResponseTb4b4b4(Tb4b4b4)Tb4b4b4)
Tb4b4b4} Tff7b72else Tb4b4b4{
Tff7b72null
Tb4b4b4}
Tb4b4b4}
Te6edf3LegacyDfuOpcodeTb4b4b4.Te6edf3PACKET_RECEIPT_NOTIF_REQ Tff7b72-Tff7b72> Tff7b72null
Te6edf3LegacyDfuOpcodeTb4b4b4.Te6edf3RECEIVE_FIRMWARE_IMAGE Tff7b72-Tff7b72> Tb4b4b4{
Te6edf3firmwareTransferStarted Tff7b72= Tff7b72true
Tff7b72null
Tb4b4b4}
Te6edf3LegacyDfuOpcodeTb4b4b4.Te6edf3VALIDATE Tff7b72-Tff7b72> Te6edf3listOfTb4b4b4(Te6edf3validateResponseTb4b4b4(Tb4b4b4)Tb4b4b4)
Te6edf3LegacyDfuOpcodeTb4b4b4.Te6edf3ACTIVATE_AND_RESET Tff7b72-Tff7b72> Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3failOnActivateWriteTb4b4b4) Tb4b4b4{
Tff7b72throw Te6edf3RuntimeExceptionTb4b4b4(Ta5d6ff"Ta5d6ffSimulated disconnect during ACTIVATE writeTa5d6ff"Tb4b4b4)
Tb4b4b4}
Tff7b72null
Tb4b4b4}
Te6edf3LegacyDfuOpcodeTb4b4b4.Te6edf3RESET Tff7b72-Tff7b72> Tff7b72null
Tff7b72else Tff7b72-Tff7b72> Tff7b72null
Tb4b4b4}
Tb4b4b4}
Tff7b72private Tff7b72fun Td2a8ffhandlePacketWriteTb4b4b4(Te6edf3dataTb4b4b4: Te6edf3ByteArrayTb4b4b4)Tb4b4b4: Te6edf3ListTff7b72<Te6edf3ByteArrayTff7b72>Tff7b72? Tb4b4b4{
T8b949e// First packet write is the 12-byte image sizes payload (after START_DFU).
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3imageSizesWrittenTb4b4b4) Tb4b4b4{
Te6edf3imageSizesWritten Tff7b72= Tff7b72true
T8b949e// Parse appSize from bytes 8..11 (LE u32).
Tff7b72if Tb4b4b4(Te6edf3dataTb4b4b4.Te6edf3size Tff7b72>Tff7b72= T79c0ff1T79c0ff2Tb4b4b4) Tb4b4b4{
Te6edf3expectedFirmwareSize Tff7b72=
Tb4b4b4(Te6edf3dataTff7b72[T79c0ff8Tff7b72]Tb4b4b4.Te6edf3toIntTb4b4b4(Tb4b4b4) Te6edf3and T79c0ff0Te6edf3xFFTb4b4b4) Te6edf3or
Tb4b4b4(Tb4b4b4(Te6edf3dataTff7b72[T79c0ff9Tff7b72]Tb4b4b4.Te6edf3toIntTb4b4b4(Tb4b4b4) Te6edf3and T79c0ff0Te6edf3xFFTb4b4b4) Te6edf3shl T79c0ff8Tb4b4b4) Te6edf3or
Tb4b4b4(Tb4b4b4(Te6edf3dataTff7b72[T79c0ff1T79c0ff0Tff7b72]Tb4b4b4.Te6edf3toIntTb4b4b4(Tb4b4b4) Te6edf3and T79c0ff0Te6edf3xFFTb4b4b4) Te6edf3shl T79c0ff1T79c0ff6Tb4b4b4) Te6edf3or
Tb4b4b4(Tb4b4b4(Te6edf3dataTff7b72[T79c0ff1T79c0ff1Tff7b72]Tb4b4b4.Te6edf3toIntTb4b4b4(Tb4b4b4) Te6edf3and T79c0ff0Te6edf3xFFTb4b4b4) Te6edf3shl T79c0ff2T79c0ff4Tb4b4b4)
Tb4b4b4}
Tff7b72return Te6edf3listOfTb4b4b4(Te6edf3startResponseTb4b4b4(Tb4b4b4)Tb4b4b4)
Tb4b4b4}
Tff7b72if Tb4b4b4(Te6edf3firmwareTransferStartedTb4b4b4) Tb4b4b4{
Te6edf3packetBytesReceived Tff7b72+Tff7b72= Te6edf3dataTb4b4b4.Te6edf3size
Te6edf3packetsSinceLastPrnTff7b72+Tff7b72+
Tff7b72val Te6edf3responses Tff7b72= Te6edf3mutableListOfTff7b72<Te6edf3ByteArrayTff7b72>Tb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3firmwareDone Tff7b72= Te6edf3packetBytesReceived Tff7b72>Tff7b72= Te6edf3expectedFirmwareSize
Tff7b72if Tb4b4b4(Te6edf3packetsSinceLastPrn Tff7b72>Tff7b72= Te6edf3LegacyDfuTransportTb4b4b4.Te6edf3PRN_INTERVAL_PACKETS Tff7b72&Tff7b72& Tff7b72!Te6edf3firmwareDoneTb4b4b4) Tb4b4b4{
Te6edf3packetsSinceLastPrn Tff7b72= T79c0ff0
Tff7b72val Te6edf3reported Tff7b72=
Tff7b72if Tb4b4b4(Te6edf3scheme Tff7b72=Tff7b72= Te6edf3LegacyResponderSchemeTb4b4b4.Te6edf3PrnUnderReportTb4b4b4) Tb4b4b4{
Te6edf3packetBytesReceived Tff7b72- T79c0ff1
Tb4b4b4} Tff7b72else Tb4b4b4{
Te6edf3packetBytesReceived
Tb4b4b4}
Te6edf3responses Tff7b72+Tff7b72= Te6edf3packetReceiptTb4b4b4(Te6edf3reportedTb4b4b4)
Tb4b4b4}
Tff7b72if Tb4b4b4(Te6edf3firmwareDoneTb4b4b4) Tb4b4b4{
Te6edf3responses Tff7b72+Tff7b72= Te6edf3successTb4b4b4(Te6edf3LegacyDfuOpcodeTb4b4b4.Te6edf3RECEIVE_FIRMWARE_IMAGETb4b4b4)
Tb4b4b4}
Tff7b72return Te6edf3responsesTb4b4b4.Te6edf3takeIf Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3isNotEmptyTb4b4b4(Tb4b4b4) Tb4b4b4}
Tb4b4b4}
T8b949e// Init-packet writes between START and COMPLETE: silent.
Tff7b72return Tff7b72null
Tb4b4b4}
Tff7b72private Tff7b72fun Td2a8ffstartResponseTb4b4b4(Tb4b4b4)Tb4b4b4: Te6edf3ByteArray Tff7b72= Tff7b72when Tb4b4b4(Te6edf3schemeTb4b4b4) Tb4b4b4{
Te6edf3LegacyResponderSchemeTb4b4b4.Te6edf3RejectStart Tff7b72-Tff7b72>
Te6edf3byteArrayOfTb4b4b4(Te6edf3LegacyDfuOpcodeTb4b4b4.Te6edf3RESPONSE_CODETb4b4b4, Te6edf3LegacyDfuOpcodeTb4b4b4.Te6edf3START_DFUTb4b4b4, Te6edf3LegacyDfuStatusTb4b4b4.Te6edf3NOT_SUPPORTEDTb4b4b4)
Tff7b72else Tff7b72-Tff7b72> Te6edf3successTb4b4b4(Te6edf3LegacyDfuOpcodeTb4b4b4.Te6edf3START_DFUTb4b4b4)
Tb4b4b4}
Tff7b72private Tff7b72fun Td2a8ffinitResponseTb4b4b4(Tb4b4b4)Tb4b4b4: Te6edf3ByteArray Tff7b72= Tff7b72when Tb4b4b4(Te6edf3schemeTb4b4b4) Tb4b4b4{
Te6edf3LegacyResponderSchemeTb4b4b4.Te6edf3RejectInit Tff7b72-Tff7b72>
Te6edf3byteArrayOfTb4b4b4(
Te6edf3LegacyDfuOpcodeTb4b4b4.Te6edf3RESPONSE_CODETb4b4b4,
Te6edf3LegacyDfuOpcodeTb4b4b4.Te6edf3INIT_DFU_PARAMSTb4b4b4,
Te6edf3LegacyDfuStatusTb4b4b4.Te6edf3OPERATION_FAILEDTb4b4b4,
Tb4b4b4)
Tff7b72else Tff7b72-Tff7b72> Te6edf3successTb4b4b4(Te6edf3LegacyDfuOpcodeTb4b4b4.Te6edf3INIT_DFU_PARAMSTb4b4b4)
Tb4b4b4}
Tff7b72private Tff7b72fun Td2a8ffvalidateResponseTb4b4b4(Tb4b4b4)Tb4b4b4: Te6edf3ByteArray Tff7b72= Tff7b72when Tb4b4b4(Te6edf3schemeTb4b4b4) Tb4b4b4{
Te6edf3LegacyResponderSchemeTb4b4b4.Te6edf3RejectValidate Tff7b72-Tff7b72>
Te6edf3byteArrayOfTb4b4b4(Te6edf3LegacyDfuOpcodeTb4b4b4.Te6edf3RESPONSE_CODETb4b4b4, Te6edf3LegacyDfuOpcodeTb4b4b4.Te6edf3VALIDATETb4b4b4, Te6edf3LegacyDfuStatusTb4b4b4.Te6edf3CRC_ERRORTb4b4b4)
Tff7b72else Tff7b72-Tff7b72> Te6edf3successTb4b4b4(Te6edf3LegacyDfuOpcodeTb4b4b4.Te6edf3VALIDATETb4b4b4)
Tb4b4b4}
Tff7b72private Tff7b72fun Td2a8ffpacketReceiptTb4b4b4(Te6edf3bytesReceivedTb4b4b4: Tffa657LongTb4b4b4)Tb4b4b4: Te6edf3ByteArray Tff7b72= Te6edf3byteArrayOfTb4b4b4(
Te6edf3LegacyDfuOpcodeTb4b4b4.Te6edf3PACKET_RECEIPTTb4b4b4,
Tb4b4b4(Te6edf3bytesReceived Te6edf3and T79c0ff0Te6edf3xFFTb4b4b4)Tb4b4b4.Te6edf3toByteTb4b4b4(Tb4b4b4)Tb4b4b4,
Tb4b4b4(Tb4b4b4(Te6edf3bytesReceived Te6edf3ushr T79c0ff8Tb4b4b4) Te6edf3and T79c0ff0Te6edf3xFFTb4b4b4)Tb4b4b4.Te6edf3toByteTb4b4b4(Tb4b4b4)Tb4b4b4,
Tb4b4b4(Tb4b4b4(Te6edf3bytesReceived Te6edf3ushr T79c0ff1T79c0ff6Tb4b4b4) Te6edf3and T79c0ff0Te6edf3xFFTb4b4b4)Tb4b4b4.Te6edf3toByteTb4b4b4(Tb4b4b4)Tb4b4b4,
Tb4b4b4(Tb4b4b4(Te6edf3bytesReceived Te6edf3ushr T79c0ff2T79c0ff4Tb4b4b4) Te6edf3and T79c0ff0Te6edf3xFFTb4b4b4)Tb4b4b4.Te6edf3toByteTb4b4b4(Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72private Tff7b72fun Td2a8ffsuccessTb4b4b4(Te6edf3opcodeTb4b4b4: Tffa657ByteTb4b4b4)Tb4b4b4: Te6edf3ByteArray Tff7b72=
Te6edf3byteArrayOfTb4b4b4(Te6edf3LegacyDfuOpcodeTb4b4b4.Te6edf3RESPONSE_CODETb4b4b4, Te6edf3opcodeTb4b4b4, Te6edf3LegacyDfuStatusTb4b4b4.Te6edf3SUCCESSTb4b4b4)
Tb4b4b4}
T8b949e/** BleConnection wrapper that swaps in the auto-responding service for `profile()` calls. */
Tff7b72private Tff7b72class T56d364AutoRespondingBleConnectionTb4b4b4(
Tff7b72private Tff7b72val Te6edf3delegateTb4b4b4: Te6edf3FakeBleConnectionTb4b4b4,
Tff7b72val Te6edf3autoServiceTb4b4b4: Te6edf3AutoRespondingLegacyServiceTb4b4b4,
Tb4b4b4) Tb4b4b4: Te6edf3BleConnection Tb4b4b4{
Tff7b72override Tff7b72val Te6edf3deviceTb4b4b4: Te6edf3BleDevice?
Tff7b72getTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3delegateTb4b4b4.Te6edf3device
Tff7b72override Tff7b72val Te6edf3deviceFlowTb4b4b4: Te6edf3StateFlowTff7b72<Te6edf3BleDevice?Tff7b72>
Tff7b72getTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3delegateTb4b4b4.Te6edf3deviceFlow
Tff7b72override Tff7b72val Te6edf3connectionStateTb4b4b4: Te6edf3StateFlowTff7b72<Te6edf3BleConnectionStateTff7b72>
Tff7b72getTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3delegateTb4b4b4.Te6edf3connectionState
Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffconnectTb4b4b4(Te6edf3deviceTb4b4b4: Te6edf3BleDeviceTb4b4b4) Tff7b72= Te6edf3delegateTb4b4b4.Te6edf3connectTb4b4b4(Te6edf3deviceTb4b4b4)
Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffconnectAndAwaitTb4b4b4(Te6edf3deviceTb4b4b4: Te6edf3BleDeviceTb4b4b4, Te6edf3timeoutTb4b4b4: Te6edf3DurationTb4b4b4) Tff7b72=
Te6edf3delegateTb4b4b4.Te6edf3connectAndAwaitTb4b4b4(Te6edf3deviceTb4b4b4, Te6edf3timeoutTb4b4b4)
Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffdisconnectTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3delegateTb4b4b4.Te6edf3disconnectTb4b4b4(Tb4b4b4)
Tff7b72override Tff7b72suspend Tff7b72fun Tff7b72<Te6edf3TTff7b72> Td2a8ffprofileTb4b4b4(
Te6edf3serviceUuidTb4b4b4: Te6edf3kotlinTb4b4b4.Te6edf3uuidTb4b4b4.Te6edf3UuidTb4b4b4,
Te6edf3timeoutTb4b4b4: Te6edf3DurationTb4b4b4,
Te6edf3setupTb4b4b4: Te6edf3suspend Te6edf3CoroutineScopeTb4b4b4.Tb4b4b4(Te6edf3BleServiceTb4b4b4) Tff7b72-Tff7b72> Te6edf3TTb4b4b4,
Tb4b4b4)Tb4b4b4: Te6edf3T Tff7b72= Te6edf3CoroutineScopeTb4b4b4(Te6edf3DispatchersTb4b4b4.Te6edf3UnconfinedTb4b4b4)Tb4b4b4.Te6edf3setupTb4b4b4(Te6edf3autoServiceTb4b4b4)
Tff7b72override Tff7b72fun Td2a8ffmaximumWriteValueLengthTb4b4b4(Te6edf3writeTypeTb4b4b4: Te6edf3BleWriteTypeTb4b4b4)Tb4b4b4: Tffa657Int? Tff7b72=
Te6edf3delegateTb4b4b4.Te6edf3maximumWriteValueLengthTb4b4b4(Te6edf3writeTypeTb4b4b4)
Tb4b4b4}
Tb4b4b4}
Served by rngit 1.5.0 - Generated in 0.21s